home *** CD-ROM | disk | FTP | other *** search
- # Source Generated with Decompyle++
- # File: in.pyc (Python 2.6)
-
- '''
- Implement a PGN reader/writer.
-
- See http://www.chessclub.com/help/PGN-spec
- '''
- __author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
- __license__ = 'GNU General Public License Version 2'
- __copyright__ = 'Copyright 2005-2006 Robert Ancell'
- import re
- RESULT_INCOMPLETE = '*'
- RESULT_WHITE_WIN = '1-0'
- RESULT_BLACK_WIN = '0-1'
- RESULT_DRAW = '1/2-1/2'
- results = {
- RESULT_INCOMPLETE: RESULT_INCOMPLETE,
- RESULT_WHITE_WIN: RESULT_WHITE_WIN,
- RESULT_BLACK_WIN: RESULT_BLACK_WIN,
- RESULT_DRAW: RESULT_DRAW }
- TAG_EVENT = 'Event'
- TAG_SITE = 'Site'
- TAG_DATE = 'Date'
- TAG_ROUND = 'Round'
- TAG_WHITE = 'White'
- TAG_BLACK = 'Black'
- TAG_RESULT = 'Result'
- TAG_TIME = 'Time'
- TAG_FEN = 'FEN'
- TAG_WHITE_TYPE = 'WhiteType'
- TAG_WHITE_ELO = 'WhiteElo'
- TAG_BLACK_TYPE = 'BlackType'
- TAG_BLACK_ELO = 'BlackElo'
- TAG_TIME_CONTROL = 'TimeControl'
- TAG_TERMINATION = 'Termination'
- PLAYER_HUMAN = 'human'
- PLAYER_AI = 'program'
- TERMINATE_ABANDONED = 'abandoned'
- TERMINATE_ADJUDICATION = 'adjudication'
- TERMINATE_DEATH = 'death'
- TERMINATE_EMERGENCY = 'emergency'
- TERMINATE_NORMAL = 'normal'
- TERMINATE_RULES_INFRACTION = 'rules infraction'
- TERMINATE_TIME_FORFEIT = 'time forfeit'
- TERMINATE_UNTERMINATED = 'unterminated'
- TOKEN_LINE_COMMENT = 'Line comment'
- TOKEN_COMMENT = 'Comment'
- TOKEN_ESCAPED = 'Escaped data'
- TOKEN_PERIOD = 'Period'
- TOKEN_TAG_START = 'Tag start'
- TOKEN_TAG_END = 'Tag end'
- TOKEN_STRING = 'String'
- TOKEN_SYMBOL = 'Symbol'
- TOKEN_RAV_START = 'RAV start'
- TOKEN_RAV_END = 'RAV end'
- TOKEN_XML = 'XML'
- TOKEN_NAG = 'NAG'
-
- class Error(Exception):
- '''PGN exception class'''
- pass
-
-
- class PGNParser:
- '''
- '''
- STATE_IDLE = 'IDLE'
- STATE_TAG_NAME = 'TAG_NAME'
- STATE_TAG_VALUE = 'TAG_VALUE'
- STATE_TAG_END = 'TAG_END'
- STATE_MOVETEXT = 'MOVETEXT'
- STATE_RAV = 'RAV'
- STATE_XML = 'XML'
-
- def __init__(self, maxGames = -1):
- expressions = [
- '\\%.*',
- ';.*',
- '\\{',
- '".*"',
- '[a-zA-Z0-9\\*\\_\\+\\#\\=\\:\\-\\/]+',
- '\\[',
- '\\]',
- '\\$[0-9]{1,3}',
- '\\(',
- '\\)',
- '\\<.*\\>',
- '[.]+']
- self.regexp = re.compile('|'.join(expressions))
- self.tokens = {
- ';': TOKEN_LINE_COMMENT,
- '{': TOKEN_COMMENT,
- '[': TOKEN_TAG_START,
- ']': TOKEN_TAG_END,
- '"': TOKEN_STRING,
- '.': TOKEN_PERIOD,
- '$': TOKEN_NAG,
- '(': TOKEN_RAV_START,
- ')': TOKEN_RAV_END,
- '<': TOKEN_XML,
- '%': TOKEN_ESCAPED }
- for c in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*':
- self.tokens[c] = TOKEN_SYMBOL
-
- self.games = []
- self.maxGames = maxGames
- self.comment = None
- self.state = self.STATE_IDLE
- self.game = PGNGame()
- self.tagName = None
- self.tagValue = None
- self.prevTokenIsMoveNumber = False
- self.currentMoveNumber = 0
- self.ravDepth = 0
-
-
- def _parseTokenMovetext(self, tokenType, data):
- '''
- '''
- if tokenType is TOKEN_SYMBOL:
- if self.ravDepth != 0:
- return None
- if results.has_key(data):
- self.games.append(self.game)
- self.game = PGNGame()
- self.prevTokenIsMoveNumber = False
- self.currentMoveNumber = 0
- self.ravDepth = 0
- self.state = self.STATE_IDLE
- else:
-
- try:
- moveNumber = int(data)
- except ValueError:
- self.ravDepth != 0
- self.ravDepth != 0
- move = PGNMove()
- move.number = self.currentMoveNumber
- move.move = data
- self.game.addMove(move)
- self.currentMoveNumber += 1
- except:
- self.ravDepth != 0
-
- self.prevTokenIsMoveNumber = True
- expected = self.currentMoveNumber / 2 + 1
- if moveNumber != expected:
- raise Error('Expected move number %i, got %i' % (expected, moveNumber))
- moveNumber != expected
- elif tokenType is TOKEN_NAG:
- if self.ravDepth != 0:
- return None
- move = self.game.getMove(self.currentMoveNumber)
- move.nag = data
- elif tokenType is TOKEN_PERIOD:
- if self.ravDepth != 0:
- return None
- if self.prevTokenIsMoveNumber is False:
- raise Error('Unexpected period')
- self.prevTokenIsMoveNumber is False
- elif tokenType is TOKEN_RAV_START:
- self.ravDepth += 1
- return None
- self
- if tokenType is TOKEN_RAV_END:
- self.ravDepth -= 1
- return None
- raise Error('Unknown token %s in movetext' % str(tokenType))
-
-
- def parseToken(self, tokenType, data):
- '''
- '''
- if tokenType is TOKEN_LINE_COMMENT or tokenType is TOKEN_COMMENT:
- if self.currentMoveNumber > 0:
- move = self.game.getMove(self.currentMoveNumber)
- move.comment = data[1:-1]
-
- return None
- if self.state is self.STATE_MOVETEXT:
- self._parseTokenMovetext(tokenType, data)
- elif self.state is self.STATE_IDLE:
- if tokenType is TOKEN_TAG_START:
- self.state = self.STATE_TAG_NAME
- return None
- if tokenType is TOKEN_SYMBOL:
- self.whiteMove = None
- self.prevTokenIsMoveNumber = False
- self.ravDepth = 0
- self.state = self.STATE_MOVETEXT
- self._parseTokenMovetext(tokenType, data)
- elif tokenType is TOKEN_ESCAPED:
- pass
- else:
- raise Error('Unexpected token %s' % str(tokenType))
- tokenType is TOKEN_SYMBOL
- if self.state is self.STATE_TAG_NAME:
- if tokenType is TOKEN_SYMBOL:
- self.tagName = data
- self.state = self.STATE_TAG_VALUE
- else:
- raise Error('Got a %s token, expecting a %s token' % (repr(tokenType), repr(TOKEN_SYMBOL)))
- tokenType is TOKEN_SYMBOL
- if self.state is self.STATE_TAG_VALUE:
- if tokenType is TOKEN_STRING:
- self.tagValue = data[1:-1]
- self.state = self.STATE_TAG_END
- else:
- raise Error('Got a %s token, expecting a %s token' % (repr(tokenType), repr(TOKEN_STRING)))
- tokenType is TOKEN_STRING
- if self.state is self.STATE_TAG_END:
- if tokenType is TOKEN_TAG_END:
- self.game.setTag(self.tagName, self.tagValue)
- self.state = self.STATE_IDLE
- else:
- raise Error('Got a %s token, expecting a %s token' % (repr(tokenType), repr(TOKEN_TAG_END)))
- tokenType is TOKEN_TAG_END
-
-
- def parseLine(self, line):
- '''Parse a line from a PGN file.
-
- Return an array of tokens extracted from the line.
- '''
- while len(line) > 0:
- if self.comment is not None:
- end = line.find('}')
- if end < 0:
- self.comment += line
- return True
- comment = self.comment + line[:end]
- self.comment = None
- self.parseToken(TOKEN_COMMENT, comment)
- line = line[end + 1:]
- continue
-
- for match in self.regexp.finditer(line):
- text = line[match.start():match.end()]
- if text == '{':
- line = line[match.end():]
- self.comment = ''
- break
- continue
-
- try:
- tokenType = self.tokens[text[0]]
- except KeyError:
- raise Error('Unknown token %s' % repr(text))
-
- self.parseToken(tokenType, text)
-
- if self.comment is None:
- return True
- continue
- self.comment is None
-
-
- def complete(self):
- if len(self.game.moves) > 0:
- self.games.append(self.game)
-
-
-
-
- class PGNMove:
- '''
- '''
- move = ''
- comment = ''
- nag = ''
-
-
- class PGNGame:
- '''
- '''
- _strTags = [
- TAG_EVENT,
- TAG_SITE,
- TAG_DATE,
- TAG_ROUND,
- TAG_WHITE,
- TAG_BLACK,
- TAG_RESULT]
-
- def __init__(self):
- self.tagsByName = { }
- self.setTag(TAG_EVENT, '?')
- self.setTag(TAG_SITE, '?')
- self.setTag(TAG_DATE, '????.??.??')
- self.setTag(TAG_ROUND, '?')
- self.setTag(TAG_WHITE, '?')
- self.setTag(TAG_BLACK, '?')
- self.setTag(TAG_RESULT, '*')
- self.moves = []
-
-
- def getLines(self):
- lines = []
- otherTags = list(set(self.tagsByName).difference(self._strTags))
- for name in self._strTags + otherTags:
- value = self.tagsByName[name]
- lines.append('[' + name + ' ' + self._makePGNString(value) + ']')
-
- lines.append('')
- tokens = []
- moveNumber = 0
- for m in self.moves:
- if moveNumber % 2 == 0:
- tokens.append('%i.' % (moveNumber / 2 + 1))
-
- moveNumber += 1
- tokens.append(m.move)
- if m.nag != '':
- tokens.append(m.nag)
-
- if m.comment != '':
- tokens.append('{' + m.comment + '}')
- continue
-
- tokens.append(self.tagsByName[TAG_RESULT])
- line = ''
- for t in tokens:
- if line == '':
- x = t
- else:
- x = ' ' + t
- if len(line) + len(x) >= 80:
- lines.append(line)
- line = t
- continue
- line += x
-
- lines.append(line)
- return lines
-
-
- def setTag(self, name, value):
- """Set a PGN tag.
-
- 'name' is the name of the tag to set (string).
- 'value' is the value to set the tag to (string) or None to delete the tag.
-
- Tag names cannot contain whitespace.
-
- Deleting a tag that does not exist has no effect.
-
- Deleting a STR tag or setting one to an invalid value will raise an Error exception.
- """
- if self._isValidTagName(name) is False:
- raise Error('%s is an invalid tag name' % str(name))
- self._isValidTagName(name) is False
- if value is None:
- if self._strTags.has_key(name):
- raise Error('%s is a PGN STR tag and cannot be deleted' % name)
- self._strTags.has_key(name)
-
- try:
- self._strTags.pop(name)
- except KeyError:
- pass
- except:
- None<EXCEPTION MATCH>KeyError
-
-
- None<EXCEPTION MATCH>KeyError
- self.tagsByName[name] = value
-
-
- def getTag(self, name, default = None):
- """Get a PGN tag.
-
- 'name' is the name of the tag to get (string).
- 'default' is the default value to return if this valid is missing (user-defined).
-
- Return the value of the tag (string) or the default if the tag does not exist.
- """
-
- try:
- return self.tagsByName[name]
- except KeyError:
- return default
-
-
-
- def addMove(self, move):
- self.moves.append(move)
-
-
- def getMove(self, moveNumber):
- return self.moves[moveNumber - 1]
-
-
- def getMoves(self):
- return self.moves
-
-
- def __str__(self):
- string = ''
- for tag, value in self.tagsByName.iteritems():
- string += '%s = %s\n' % (tag, value)
-
- string += '\n'
- number = 1
- moves = self.moves
- while len(moves) >= 2:
- string += '%3i. %s %s\n' % (number, moves[0].move, moves[1].move)
- number += 1
- moves = moves[2:]
- if len(moves) > 0:
- string += '%3i. %s\n' % (number, moves[0].move)
-
- return string
-
-
- def _makePGNString(self, string):
- '''Make a PGN string.
-
- \'string\' is the string to convert to a PGN string (string).
-
- All characters are valid and quotes are escaped with \'"\'.
-
- Return the string surrounded with quotes. e.g. \'Mike "Dog" Smith\' -> \'"Mike "Dog" Smith"\'
- '''
- pgnString = string
- pgnString.replace('"', '\\"')
- return '"' + pgnString + '"'
-
-
- def _isValidTagName(self, name):
- """Valid a PGN tag name.
-
- 'name' is the tag name to validate (string).
-
- Tags can only contain the characters, a-Z A-Z and _.
-
- Return True if this is a valid tag name otherwise return False.
- """
- if name is None or len(name) == 0:
- return False
- validCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
- for c in name:
- if validCharacters.find(c) < 0:
- return False
-
- return True
-
-
-
- class PGN:
- '''
- '''
- __games = None
-
- def __init__(self, fileName = None, maxGames = None):
- """Create a PGN reader/writer.
-
- 'fileName' is the file to load the PGN from or None to generate an empty PGN file.
- 'maxGames' is the maximum number of games to load from the file or None
- to load the whole file. (int, Only applicable if a filename is supplied).
- """
- self._PGN__games = []
- if fileName is not None:
- self._PGN__load(fileName, maxGames)
-
-
-
- def addGame(self):
- '''Add a new game to the PGN file.
-
- Returns the PGNGame instance to modify'''
- game = PGNGame()
- self._PGN__games.append(game)
- return game
-
-
- def getGame(self, index):
- """Get a game from the PGN file.
-
- 'index' is the game index to get (integer, 0-N).
-
- Return this PGN game or raise an IndexError if no game with this index.
- """
- return self._PGN__games[index]
-
-
- def save(self, fileName):
- """Save the PGN file.
-
- 'fileName' is the name of the file to save to.
- """
- f = file(fileName, 'w')
- f.write('; PGN saved game generated by glChess\n')
- f.write('; http://glchess.sourceforge.net\n')
- for game in self._PGN__games:
- f.write('\n')
- for line in game.getLines():
- f.write(line + '\n')
-
-
- f.close()
-
-
- def __len__(self):
- return len(self._PGN__games)
-
-
- def __getitem__(self, index):
- return self._PGN__games[index]
-
-
- def __getslice__(self, start, end):
- return self._PGN__games[start:end]
-
-
- def __load(self, fileName, maxGames = None):
- '''
- '''
- f = file(fileName, 'r')
- p = PGNParser(maxGames)
- lineNumber = 0
-
- try:
- for line in f.readlines():
- lineNumber += 1
- p.parseLine(line)
-
- p.complete()
- except Error:
- e = None
- raise Error('Error on line %d: %s' % (lineNumber, e.message))
-
- self._PGN__games = p.games
- if len(self._PGN__games) == 0:
- raise Error('Empty PGN file')
- len(self._PGN__games) == 0
- f.close()
-
-
- if __name__ == '__main__':
- import time
-
- def test(fileName, maxGames = None):
- s = time.time()
- p = PGN(fileName, maxGames)
- print time.time() - s
- number = 1
- games = p[:]
-
-
-